/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.codefactory.geneticalgorithm.optimizer;

import com.codefactory.geneticalgorithm.App;
import com.codefactory.geneticalgorithm.MachineFitnessFunction;
import com.codefactory.geneticalgorithm.MachineInfo;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jgap.Chromosome;
import org.jgap.Configuration;
import org.jgap.DefaultFitnessEvaluator;
import org.jgap.FitnessFunction;
import org.jgap.Gene;
import org.jgap.Genotype;
import org.jgap.IChromosome;
import org.jgap.InvalidConfigurationException;
import org.jgap.event.EventManager;
import org.jgap.impl.BooleanGene;
import org.jgap.impl.CompositeGene;
import org.jgap.impl.CrossoverOperator;
import org.jgap.impl.GaussianRandomGenerator;
import org.jgap.impl.MutationOperator;
import org.jgap.impl.StandardPostSelector;

/**
 * The class is responsible for optimization using genetic algorithm.
 * @author kurbatov
 */
public class GeneticOptimizer implements Optimizer{
    
    private List<MachineInfo> machines;

    private Genotype population;
    
    public GeneticOptimizer(List<MachineInfo> machines) {
        this.machines = machines;
        Configuration cfg = new Configuration("MachineGeneticOptimizer");
        FitnessFunction fitnessFunction = new MachineFitnessFunction();
        Gene[] genes = new Gene[machines.size()];
        try {
            for (int i = 0; i < genes.length; i++) {
                CompositeGene machineSchedule = new CompositeGene(cfg);
                CompositeGene machineRepairSchedule = new CompositeGene(cfg);
                CompositeGene machineMaintenanceSchedule = new CompositeGene(cfg);
                for(int month = 0; month < 12; month++){
                    machineRepairSchedule.addGene(new BooleanGene(cfg));
                    machineMaintenanceSchedule.addGene(new BooleanGene(cfg));
                }
                machineSchedule.addGene(machineRepairSchedule);
                machineSchedule.addGene(machineMaintenanceSchedule);
                machineSchedule.setApplicationData(machines.get(i));
                genes[i] = machineSchedule;
            }
            Chromosome chromosome = new Chromosome(cfg, genes);
            cfg.setSampleChromosome(chromosome);
            cfg.setPopulationSize(500);
            
            cfg.setFitnessFunction(fitnessFunction);
            cfg.setFitnessEvaluator(new DefaultFitnessEvaluator());
            cfg.addNaturalSelector(new StandardPostSelector(cfg), false);
            cfg.setRandomGenerator(new GaussianRandomGenerator());
            cfg.setEventManager(new EventManager());
            cfg.addGeneticOperator(new CrossoverOperator(cfg));
            //cfg.addGeneticOperator(new MutationOperator(cfg));
            population = Genotype.randomInitialGenotype(cfg);
        } catch (InvalidConfigurationException ex) {
            Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    
    @Override
    public void optimize(){
        population.evolve();//производим один цикл оптимизации решения
    }
    
    @Override
    public Map<MachineInfo, Vector<Vector<Boolean>>> getSchedule(){
        Map<MachineInfo, Vector<Vector<Boolean>>> result = new HashMap<MachineInfo, Vector<Vector<Boolean>>>();
        IChromosome best = population.getFittestChromosome();//берём лучшую особь
        for(Gene gene : best.getGenes()){
            result.put((MachineInfo)gene.getApplicationData(), (Vector<Vector<Boolean>>)gene.getAllele());
        }
        System.out.println("Best solution so far: ");
        System.out.println(best);
        return result;
    }
    
}
